﻿namespace Refract.Extensions
{
    using System.ComponentModel.Composition;
    using System.ComponentModel.Composition.Hosting;
    using System.ComponentModel.Composition.Primitives;
    using System.Linq;
    using System.Text;

    /// <summary>
    /// Extends MEF containers and catalogs
    /// </summary>
    public static class ExportProviderExtensions
    {
        #region Methods

        /// <summary>
        /// Composes a part, satisfying all imports and importing it into the container.
        /// </summary>
        /// <param name="container">The container.</param>
        /// <param name="part">The part.</param>
        public static void ComposePart(this CompositionContainer container, object part)
        {
            CompositionBatch batch = new CompositionBatch();
            batch.AddPart(part);
            container.Compose(batch);
        }

        /// <summary>
        /// Exports a new value.
        /// </summary>
        /// <typeparam name="TInterface">The type of the interface.</typeparam>
        /// <typeparam name="TType">The type of the type.</typeparam>
        /// <param name="provider">The provider.</param>
        /// <returns></returns>
        public static TInterface ExportNewValue<TInterface, TType>(this ExportProvider provider)
            where TType : TInterface
        {
            return (TInterface)ExportNewValue<TType>(provider);
        }

        /// <summary>
        /// Exports a new value.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="provider">The provider.</param>
        /// <returns></returns>
        public static T ExportNewValue<T>(this ExportProvider provider)
        {
            return (T) ExportNewValue(provider, typeof (T).FullName);
        }

        /// <summary>
        /// Exports a new value.
        /// </summary>
        /// <param name="provider">The provider.</param>
        /// <param name="typeName">Name of the type.</param>
        /// <param name="contractName">Name of the contract.</param>
        /// <returns></returns>
        public static object ExportNewValue(this ExportProvider provider, string typeName, string contractName)
        {
            var definition = new ContractBasedImportDefinition(contractName, typeName, null, ImportCardinality.ExactlyOne, false, false, CreationPolicy.NonShared);
            return provider.GetExports(definition).First().Value;
        }

        /// <summary>
        /// Exports a new value.
        /// </summary>
        /// <param name="provider">The provider.</param>
        /// <param name="typeName">Name of the type.</param>
        /// <returns></returns>
        public static object ExportNewValue(this ExportProvider provider, string typeName)
        {
            return ExportNewValue(provider, typeName, typeName);
        }

        /// <summary>
        /// Exports a new value.
        /// </summary>
        /// <param name="provider">The provider.</param>
        /// <param name="typeName">Name of the type.</param>
        /// <returns></returns>
        public static object ExportNewValue(this ComposablePartCatalog provider, string typeName)
        {
            return ExportNewValue(provider, typeName, typeName);
        }

        /// <summary>
        /// Exports a new value.
        /// </summary>
        /// <param name="provider">The provider.</param>
        /// <param name="typeName">Name of the type.</param>
        /// <param name="contractName">Name of the contract.</param>
        /// <returns></returns>
        public static object ExportNewValue(this ComposablePartCatalog provider, string typeName, string contractName)
        {
            var definition = new ContractBasedImportDefinition(contractName, typeName, null, ImportCardinality.ExactlyOne, false, false, CreationPolicy.NonShared);

            ComposablePart part = provider.GetExports(definition).First().Item1.CreatePart();
            return part.GetExportedValue(part.ExportDefinitions.First());
        }

        /// <summary>
        /// Lists the parts in a catalog.
        /// </summary>
        /// <param name="catalog">The catalog.</param>
        /// <returns></returns>
        public static string ListParts(this ComposablePartCatalog catalog)
        {
            StringBuilder x = new StringBuilder();
                foreach (ComposablePartDefinition part in catalog.Parts)
                {
                    foreach (ExportDefinition definition in part.ExportDefinitions)
                    {
                        x.Append("ContractName - " + definition.ContractName + " : ");
                        foreach (string key in definition.Metadata.Keys)
                        {
                            x.Append(key + " - " + definition.Metadata[key] + " ; ");

                        }
                        x.Append(". ");
                    }
                }
                x.AppendLine();

            return x.ToString();
        }

        #endregion Methods
    }
}